namespace Dropthings.Test.Rules
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Net;
    using System.Text;
    using System.Text.RegularExpressions;

    using Dropthings.Test.Plugins;

    using Microsoft.VisualStudio.TestTools.WebTesting;

    public enum ControlCategory
    {
        GRID,
        INPUT,
        SCRIPTMANAGER,
        SELECT,
        POSTBACK,
        UPDATEPANEL,
        HIDDEN
    }

    internal static class RuleHelper
    {

        #region Fields

        private static Regex _FindContextKey = new Regex(@"{{(.*?)}}",
            RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.CultureInvariant);

        #endregion Fields

        #region Methods

        public static Cookie GetCookie(string name, string domain, CookieCollection cookies)
        {
            // Same cookie can be issued on different domains. So, we need to check if there's any domain expected
            // and if domain is specified, get the cookie for that domain
            if (!string.IsNullOrEmpty(domain))
            {
                // Find the cookir for the specific domain
                foreach (Cookie cookie in cookies)
                {
                    if (cookie.Name == name && cookie.Domain == domain)
                        return cookie;
                }

                // Cookie not found for the specified domain
                return null;
            }
            else
            {
                // No domain specified, so the first cookie that matches the domain will be sent
                return cookies[name];
            }
        }

        public static bool NotAlreadyDone(WebTestContext context, string stepKey, Action callback)
        {
            int stepNo = (int)context[ASPNETWebTestPlugin.STEP_NO_KEY];
            string keyName = stepKey + "." + stepNo;
            if (context.ContainsKey(keyName))
            {
                return false;
            }
            else
            {
                callback();
                context[keyName] = true;
                return true;
            }
        }

        public static void NotAlreadyExtracted<TExtractionRule>(ExtractionRuleReferenceCollection rules, Action callback)
            where TExtractionRule : ExtractionRule
        {
            bool found = false;
            foreach (ExtractionRuleReference ruleRef in rules)
            {
                if (ruleRef.Type == typeof(TExtractionRule))
                {
                    found = true;
                    break;
                }
            }

            if (!found)
                callback();
        }
        /// <summary>
        /// Enables you to access any control in the http response with its Server ID by generating ID selector (for example: #txtName) inside web test context and map it to the prodived full control ID
        /// </summary>
        /// <param name="context">Web test context</param>
        /// <param name="fullControlID">For example: "ctl00$ContentPlaceHolder1$Details1$txtName"</param>
        /// <returns></returns>
        public static string PlaceIDSelector(WebTestContext context, string fullControlID)
        {
            return PlaceIDSelector(context, fullControlID, null);
        }
        /// <summary>
        /// Enables you to access any control in the http response with its Server ID by generating ID selector (for example: #txtName) inside web test context and map it to the prodived full control ID
        /// </summary>
        /// <param name="context">Web test context</param>
        /// <param name="fullControlID">For example: "ctl00$ContentPlaceHolder1$Details1$txtName"</param>
        /// <param name="controlValue">The value which control has</param>
        /// <returns></returns>
        public static string PlaceIDSelector(WebTestContext context, string fullControlID, string controlValue)
        {
            if (string.IsNullOrEmpty(fullControlID))
                throw new NullReferenceException("Cannot map ID selector to empty control id");

            string lastPartOfName = fullControlID.Substring(fullControlID.LastIndexOf('$') + 1);

            int i = 1;
            string idSelector = "#" + lastPartOfName;

            while (context.ContainsKey(idSelector))
            {
                var itemValue = (IComparable)context[idSelector];

                if (itemValue.Equals(fullControlID))
                {
                    break; // We already have this exact control in Context.
                }
                else
                {
                    i++; // Another instance of the same Control. Try another number in Key.
                    idSelector = "#" + lastPartOfName + "." + i;
                }
            }

            SetContextParameter(context, fullControlID, controlValue, idSelector);
            return idSelector;
        }

        private static void SetContextParameter(WebTestContext context, string fullControlID, string controlValue, string selector)
        {
            if (!context.ContainsKey("RequestLevelParameters"))
            {
                context.Add("RequestLevelParameters", new List<string>());
            }
            var requestLevelParameters = context["RequestLevelParameters"] as List<string>;

            context[selector] = fullControlID;
            if (!string.IsNullOrEmpty(controlValue))
            {
                context[selector + ".Value"] = controlValue;
                requestLevelParameters.Add(selector + ".Value");
            }

            
            requestLevelParameters.Add(selector);
        }
        /// <summary>
        /// Enables you to access any control in the http response with its index by generating Index selector (for example: $GRID.0) inside web test context and map it to the provided full control ID
        /// </summary>
        /// <param name="context">Web test context</param>
        /// <param name="keyPrefix">For example: "INPUT"</param>
        /// <param name="fullControlID">For example: "ctl00$ContentPlaceHolder1$Details1$txtName"</param>
        /// <returns></returns>
        public static string PlaceIndexSelector(WebTestContext context, ControlCategory controlType, string fullControlID)
        {
            return PlaceIndexSelector(context, controlType, fullControlID, null);
        }
        /// <summary>
        /// Enables you to access any control in the http response with its index by generating Index selector (for example: $GRID.0) inside web test context and map it to the provided full control ID
        /// </summary>
        /// <param name="context">Web test context</param>
        /// <param name="keyPrefix">For example: "INPUT"</param>
        /// <param name="fullControlID">For example: "ctl00$ContentPlaceHolder1$Details1$txtName"</param>
        /// <param name="controlValue">The value which control has</param>
        /// <returns></returns>
        public static string PlaceIndexSelector(WebTestContext context, ControlCategory controlType, string fullControlID, string controlValue)
        {
            if (string.IsNullOrEmpty(fullControlID))
                throw new NullReferenceException("Cannot map Index selector to empty control id");

            string keyPrefix = "$" + controlType + ".";

            int i = 0;
            string indexSelector = keyPrefix + i;

            while (context.ContainsKey(indexSelector))
            {
                var itemValue = (IComparable)context[indexSelector];

                if (itemValue.Equals(fullControlID))
                {
                    break; // We already have this exact control in Context.
                }
                else
                {
                    i++; // Another instance of the same Control. Try another number in Key.
                    indexSelector = keyPrefix + i;
                }
            }

            SetContextParameter(context, fullControlID, controlValue, indexSelector);

            return indexSelector;
        }

        public static string ResolveContextValue(WebTestContext context, string value)
        {
            return _FindContextKey.Replace(value, new MatchEvaluator((m) =>
            {
                string keyName = m.Groups[1].Value;
                if (context.ContainsKey(keyName))
                    return context[keyName].ToString();
                else
                    throw new ApplicationException(string.Format("Key {0} not found in Context", keyName));
            }));
        }

        public static void SetParameter(FormPostParameterCollection postParams, string name, string value, bool encode)
        {
            foreach (FormPostParameter postParam in postParams)
            {
                if (postParam.Name == name)
                {
                    postParam.Value = value;
                    return;
                }
            }

            postParams.Add(name, value, encode);
        }

        public static void WhenAspNetResponse(WebTestResponse response, Action callback)
        {
            if (response.StatusCode == System.Net.HttpStatusCode.OK)
            {
                if (response.ContentType.Contains("text/html")
                    || response.ContentType.Contains("text/plain"))
                {
                    callback();
                }
            }
        }

        #endregion Methods
    }
}